﻿using System;
using System.Collections.Generic;
using System.Text;
//new
using System.Threading;
using System.Net.Sockets;
using System.IO;
using JRCore.protocol;
using JRCore.enums;

namespace JRCore.tools
{
    /// <summary>
    /// 客户端
    /// </summary>
    public class Client
    {
        private bool m_exit;
        private Enum_TcpWorkMode m_mode;
        private string m_ServerListenIP;
        private int m_ServerListenPort;
        private TcpClient m_Client;
        private Timer m_Timer;
        private NetworkStream m_ns;
        private BinaryReader m_br;
        private BinaryWriter m_bw;
        private Msg m_send;
        private Thread m_ReadThread;
        private Msg m_read;
        private int m_TimeOut;
        /// <summary>
        /// 收到消息的处理函数，解析服务器反馈的处理结果，或者处理对服务器返回信息
        /// Read/ReadSend/SendRead/模式下需要重写本函数，Send模式不需要
        /// 重新委托此函数用以对反馈消息和请求信息进行验证和处理：
        /// 1.反馈消息类型是否与请求消息类型匹配（反馈比请求大1）
        /// 2.反馈uuid是否和请求uuid一致
        /// 3.反馈信息的密钥是否正确(如果协议中需要)。
        /// 4.服务器的处理结果
        /// 5.进行自定义协议的处理，并反馈结果
        /// </summary>
        /// <param name="send">Read模式send不使用,ReadSend模式send为输出,SendRead模式send为输入</param>
        /// <param name="read">Read/ReadSend/SendRead模式，read为对方发送的消息</param>
        /// <param name="error">对外输出错误的提示信息</param>
        /// <returns>服务器反馈的处理结果，或者是服务器返回信息的处理结果</returns>
        public delegate bool RecordReceived(ref Msg send,Msg read, ref string error);
        /// <summary>
        /// 实现此委托,实现对不同反馈消息的处理
        /// </summary>
        public RecordReceived OnRecordReceived;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ServerListenIP">服务端监听IP</param>
        /// <param name="ServerListenPort">服务端监听Port</param>
        /// <param name="mode">工作模式</param>
        /// <param name="timeout">读写超时时间（ms），赋值0代表阻塞式读取和发送</param>
        public Client(string ServerListenIP, int ServerListenPort,Enum_TcpWorkMode mode,int timeout)
        {
            try
            {
                m_TimeOut = timeout;
                m_ServerListenIP = ServerListenIP;
                m_ServerListenPort = ServerListenPort;
                m_mode = mode;
                m_exit = false;
                m_send = new Msg();
                m_read = new Msg();
                OnRecordReceived = new RecordReceived(RecordReceivedFunc);
                m_Timer = new Timer(new TimerCallback(TimerCallbackFunc), null, 0, 10000);
            }
            catch { }
        }

        /// <summary>
        /// 尝试一次连接
        /// </summary>
        private void TryConnect()
        {
            try
            {
                if (m_Client == null)
                {
                    m_Client = new TcpClient();
                    m_Client.Connect(m_ServerListenIP, m_ServerListenPort);
                    if (m_TimeOut > 0)
                    {
                        m_Client.SendTimeout = m_TimeOut;
                        m_Client.ReceiveTimeout = m_TimeOut;
                    }

                    if (m_Client.Client.Connected == true)
                    {
                        m_ns = m_Client.GetStream();
                        if (m_ns != null)
                        {
                            m_br = new BinaryReader(m_ns);
                            m_bw = new BinaryWriter(m_ns);
                            if ((m_mode == Enum_TcpWorkMode.Read)||(m_mode == Enum_TcpWorkMode.ReadSend))
                            {
                                m_ReadThread = new Thread(new ThreadStart(ReadThreadFunc));
                                m_ReadThread.IsBackground = true;
                                m_ReadThread.Start();
                            }
                        }
                        Console.WriteLine("建立连接" + m_ServerListenIP + ":" + m_ServerListenPort.ToString());
                    }
                }
                if (m_Client.Client.Connected == false)
                {
                    ShutDown();
                }
            }
            catch
            {
                ShutDown();
            }
        }

        private void ReadThreadFunc()
        {
            try
            {
                byte[] buffer = null;
                while ((m_exit == false) && (m_Client.Client.Connected == true))
                {
                    bool feedback = false;
                    string error = "";
                    bool flag = false;

                    switch (m_mode)
                    {
                        case Enum_TcpWorkMode.Read:
                            m_read.type = (Enum_ProtocolType)m_br.ReadByte();
                            m_read.uuid = Encoding.UTF8.GetString(m_br.ReadBytes(32));
                            feedback = (m_br.ReadByte() == 0) ? false : true;
                            m_read.feedback = feedback;
                            m_read.length = m_br.ReadInt32();
                            m_read.data = m_br.ReadBytes(m_read.length);
                            flag = OnRecordReceived.Invoke(ref m_send, m_read, ref error);
                            break;
                        case Enum_TcpWorkMode.ReadSend:
                            m_read.type = (Enum_ProtocolType)m_br.ReadByte();
                            m_read.uuid = Encoding.UTF8.GetString(m_br.ReadBytes(32));
                            feedback = (m_br.ReadByte() == 0) ? false : true;
                            m_read.feedback = feedback;
                            m_read.length = m_br.ReadInt32();
                            m_read.data = m_br.ReadBytes(m_read.length);
                            flag = OnRecordReceived.Invoke(ref m_send, m_read, ref error);
                            if ((feedback) && (m_send != null) && (m_send.data != null))
                            {
                                lock (m_bw)
                                {
                                    buffer = new byte[1 + 32 + 1 + 4 + m_send.length];
                                    buffer[0] = (byte)m_send.type;
                                    Array.Copy(Encoding.UTF8.GetBytes(m_send.uuid), 0, buffer, 1, 32);
                                    buffer[33] = (m_send.feedback ? (byte)1 : (byte)0);
                                    Array.Copy(IntToBytes(m_send.length), 0, buffer, 34, 4);
                                    Array.Copy(m_send.data, 0, buffer, 38, m_send.length);
                                    m_bw.Write(buffer);
                                }
                                // Console.WriteLine("反馈消息给" + m_ServerListenIP + "type=" + m_send.type.ToString() + "uuid=" + m_send.uuid + "feedback=" + m_send.feedback.ToString());
                            }
                            break;
                        case Enum_TcpWorkMode.Send:
                            Thread.Sleep(100);
                            break;
                        case Enum_TcpWorkMode.SendRead:
                            Thread.Sleep(100);
                            break;
                        default:
                            break;
                    }
                    GC.Collect();
                    Thread.Sleep(200);
                }
            }
            catch (Exception er)
            {
                ShutDown();
                Console.WriteLine("ReadThreadFunc()" + er.Message);
            }
        }

        private void TimerCallbackFunc(object obj)
        {
            TryConnect();
        }

        /// <summary>
        /// 发送
        /// </summary>
        /// <param name="msg">信息</param>
        /// <param name="error">错误提示</param>
        /// <returns>true-上传成功，false-上传失败</returns>
        public bool Send(Msg msg,ref string error)
        {
            bool ans = false;
            byte[] buffer = null;
            try
            {
                if ((m_Client != null) && (m_Client.Client != null) && (m_Client.Client.Connected == true) && (m_br != null) && (m_bw != null))
                {
                    if ((msg.data != null) && (msg.data.Length > 0))
                    {

                        switch (m_mode)
                        { 
                            case Enum_TcpWorkMode.Send:
                                lock (m_bw)
                                {
                                    buffer = new byte[1 + 32 + 1 + 4 + msg.length];
                                    buffer[0] = (byte)msg.type;
                                    Array.Copy(Encoding.UTF8.GetBytes(msg.uuid), 0, buffer, 1, 32);
                                    buffer[33] = (msg.feedback ? (byte)1 : (byte)0);
                                    Array.Copy(IntToBytes(msg.length), 0, buffer, 34, 4);
                                    Array.Copy(msg.data, 0, buffer, 38, msg.length);
                                    m_bw.Write(buffer);
                                    ans = true;
                                    GC.Collect();
                                }
                                break;
                            case Enum_TcpWorkMode.SendRead:
                                lock (m_bw)
                                {
                                    buffer = new byte[1 + 32 + 1 + 4 + msg.length];
                                    buffer[0] = (byte)msg.type;
                                    Array.Copy(Encoding.UTF8.GetBytes(msg.uuid), 0, buffer, 1, 32);
                                    buffer[33] = (msg.feedback ? (byte)1 : (byte)0);
                                    Array.Copy(IntToBytes(msg.length), 0, buffer, 34, 4);
                                    Array.Copy(msg.data, 0, buffer, 38, msg.length);
                                    m_bw.Write(buffer);
                                    if (msg.feedback)
                                    {
                                        m_read.type = (Enum_ProtocolType)m_br.ReadByte();
                                        m_read.uuid = Encoding.UTF8.GetString(m_br.ReadBytes(32));
                                        m_read.feedback = (m_br.ReadByte() == 1) ? true : false;
                                        m_read.length = m_br.ReadInt32();
                                        m_read.data = m_br.ReadBytes(m_read.length);
                                        ans = OnRecordReceived.Invoke(ref msg, m_read, ref error);
                                    }
                                    else
                                    {
                                        ans = true;
                                    }
                                    GC.Collect();
                                }
                                break;
                            case Enum_TcpWorkMode.Read:
                                error = "Read模式下不能使用UPLoad()";
                                break;
                            case Enum_TcpWorkMode.ReadSend:
                                error = "ReadWrite模式下不能使用UPLoad()";
                                break;
                            default:
                                break;
                        }
                        
                    }
                }
            }
            catch(Exception er)
            { 
                ans = false;
                error = "Client.UPLoad()" + er.Message;
                Console.WriteLine(error);
                GC.Collect();
            }
            return ans;
        }

        private void ShutDown()
        {
            try
            {
                if (m_br != null)
                {
                    m_br.Close();
                    m_br = null;
                }
                if (m_bw != null)
                {
                    m_bw.Close();
                    m_bw = null;
                }
                if (m_ns != null)
                {
                    m_ns.Close();
                    m_ns = null;
                }
                if (m_Client != null)
                {
                    if (m_Client.Client != null)
                    {
                        if (m_Client.Client.Connected)
                        {
                            m_Client.Client.Shutdown(SocketShutdown.Both);
                            m_Client.Client.Close();
                        }
                    }
                    m_Client.Close();
                    m_Client = null;
                    Console.WriteLine("连接断开" + m_ServerListenIP + ":" + m_ServerListenPort.ToString());
                }
            }
            catch { }
        }

        private bool RecordReceivedFunc(ref Msg send,Msg read, ref string error)
        {
            error = "没有实现该消息处理函数";
            return false;
        }

        /// <summary>
        /// 关闭，销毁资源
        /// </summary>
        public void Dispose()
        {
            m_exit = true;
            try
            {
                if (m_ReadThread != null)
                {
                    m_ReadThread.Interrupt();
                    m_ReadThread.Abort();
                    m_ReadThread.Join(3000);
                }
            }
            catch { }

            ShutDown();

            try
            {
                if (m_Timer != null)
                {
                    m_Timer.Dispose();
                }
            }
            catch { }
        }

        private static byte[] IntToBytes(int value)
        {
            byte[] src = new byte[4];
            src[3] = (byte)((value >> 24) & 0xFF);
            src[2] = (byte)((value >> 16) & 0xFF);
            src[1] = (byte)((value >> 8) & 0xFF);
            src[0] = (byte)(value & 0xFF);
            return src;
        } 
    }
}
